﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using Asol.Reporting.Support.Components;
using Asol.Reporting.Support;
using Asol.Reporting.Editor.Editor;
using Asol.Reporting.Editor.Components;
using Asol.Reporting.Editor.Forms;
using Asol.Reporting.Editor.ComponentsLayer;

namespace Asol.Reporting.Editor.Data
{
    /// <summary>
    /// Datová obálka editovaného dokumentu. 
    /// Ta se ukládá do XML dat, tu lze renderovat do sestavy.
    /// Slouží jako zdroj/cíl dat pro editaci.
    /// </summary>
    public partial class ReportDocument : IRevertContainer
    {
        #region Zobrazování a editace : tvorba dat pro zobrazení reportu v editoru
        /// <summary>
        /// Zahájí / refreshuje editaci tohoto reportu v okně
        /// </summary>
        public void ReportEdit()
        {
            if (!this.HasForm)
            {
                using (Assistant.DisposableScope(delegate() { this.Form = null; }))
                using (Asol.Reporting.Editor.Forms.EditorForm form = new EditorForm())
                {
                    this.Form = form;
                    form.Document = this;
                    form.ShowDialog();
                }
            }
            else
            {
                this.Form.Document = this;
                this.Form.Refresh();
            }
        }
        /// <summary>
        /// Zajistí překreslení vzhledu editoru
        /// </summary>
        internal void ReDraw()
        {
            if (this.HasForm && this.RootBand != null)
                this.Form.EditorReDraw();
        }
        /// <summary>
        /// Připraví si linearizované seznamy Bands a Components pro vykreslení obsahu editoru.
        /// Seznamy připravuje do property EditorBands a EditorComponents.
        /// Tuto metodu je třeba volat 1x na začátku Draw.
        /// </summary>
        /// <param name="args"></param>
        /// <param name="visualCollapsedHeight"></param>
        internal void PrepareDraw(EditEventArgs args, float visualCollapsedHeight)
        {
            this.PrepareInnerDict();
            using (ReportBandLinearData data = new ReportBandLinearData(args, this.EditorBandHeaderWidth, visualCollapsedHeight, this.EditorInnerBandDict, this.EditorInnerComponentDict))
            {
                this.RootBand.FillEditorBandComponentList(args, data);
                ReportComponent.SortByZOrder(data.ResultComponentList);
                this.EditorBands = data.ResultBandList;
                this.EditorComponents = data.ResultComponentList;
                this.EditorDocumentSize = data.ResultDocumentSize;
            }
        }
        /// <summary>
        /// Soupis komponent pro vykreslování.
        /// Obsahuje všechny komponenty z this.EditComponentList, setříděné tak, aby reprezentovaly korektně vrstvy v editoru (selectované nahoře = na konci seznamu).
        /// </summary>
        internal IEnumerable<ReportItem> DrawComponentList { get { return this.EditorComponents; } }
        /// <summary>
        /// Rozměr dokumentu v editoru (nikoli při tisku), v logických jednotkách = milimetry.
        /// Rozměr je určen vždy po dokončení metody this.PrepareDraw() = příprava k zobrazení
        /// </summary>
        internal SizeF EditorDocumentSize { get; private set; }
        /// <summary>
        /// Soupis položek Bands, které se mají aktuálně kreslit v editoru.
        /// </summary>
        internal IEnumerable<ReportBand> EditorBands { get; private set; }
        /// <summary>
        /// Soupis položek Components, které se mají aktuálně kreslit v editoru.
        /// Komponenty pouze z viditelných Bands (jejich Band nejsou Collapsed).
        /// Soupis je připraven v metodě this.PrepareDraw()
        /// </summary>
        internal IEnumerable<ReportComponent> EditorComponents { get; private set; }
        /// <summary>
        /// Připraví vnitřní seznamy this.EditorInnerBandDict a this.EditorInnerComponentDict.
        /// Jde o Dictionary, kde Key = BandId a Value je seznam vnitřních Bands a Components.
        /// V dynamickém světě nelze tyto vnitřní prvky evidovat jako prvky vnitřních seznamů v Bands,
        /// protože požadujeme snadné přesuny do jiných Bands (změnou OwnerBandId)
        /// </summary>
        protected void PrepareInnerDict()
        {
            // Bands:
            if (this.EditorInnerBandDict == null)
                this.EditorInnerBandDict = new Dictionary<int, List<ReportBand>>();
            else if (this.EditorInnerBandDict.Count > 0)
                this.EditorInnerBandDict.Clear();

            foreach (ReportBand band in this.BandDict.Values)
            {
                band.ResetVisualBounds();
                if (band.OwnerBandId > 0)
                {
                    List<ReportBand> list;
                    if (!this.EditorInnerBandDict.TryGetValue(band.OwnerBandId, out list))
                    {
                        list = new List<ReportBand>();
                        this.EditorInnerBandDict.Add(band.OwnerBandId, list);
                    }
                    list.Add(band);
                }
            }

            // Components:
            if (this.EditorInnerComponentDict == null)
                this.EditorInnerComponentDict = new Dictionary<int, List<ReportComponent>>();
            else if (this.EditorInnerComponentDict.Count > 0)
                this.EditorInnerComponentDict.Clear();

            foreach (ReportComponent component in this.ComponentDict.Values)
            {
                component.ResetVisualBounds();
                if (component.OwnerTopBandId > 0)
                    this.AddEditorInnerComponent(component.OwnerTopBandId, component);
                if (component.OwnerBottomBandId > 0 &&  component.OwnerBottomBandId != component.OwnerTopBandId)
                    this.AddEditorInnerComponent(component.OwnerBottomBandId, component);
            }
        }
        /// <summary>
        /// Do vnitřního soupisu this.EditorInnerComponentDict přidá pro klíč bandId danou komponentu.
        /// </summary>
        /// <param name="bandId"></param>
        /// <param name="component"></param>
        private void AddEditorInnerComponent(int bandId, ReportComponent component)
        {
            List<ReportComponent> list;
            if (!this.EditorInnerComponentDict.TryGetValue(bandId, out list))
            {
                list = new List<ReportComponent>();
                this.EditorInnerComponentDict.Add(bandId, list);
            }
            list.Add(component);
        }
        /// <summary>
        /// Soupis Bands, živě vytvářený 1x v metodě this.PrepareDraw().
        /// Key = číslo BandId, Value = seznam jeho InnerBands (tedy ty Band, které jako svůj OwnerBandId uvádějí dané ID, které je Key v tomto Dictionary).
        /// </summary>
        protected Dictionary<int, List<ReportBand>> EditorInnerBandDict;
        /// <summary>
        /// Soupis Components, živě vytvářený 1x v metodě this.PrepareDraw().
        /// Key = číslo BandId, Value = seznam jejích Component (tedy ty Component, které jako svůj OwnerBandId uvádějí dané ID, které je Key v tomto Dictionary).
        /// </summary>
        protected Dictionary<int, List<ReportComponent>> EditorInnerComponentDict;
        /// <summary>
        /// Vrátí vizuální šířku (v pixelech) pro Band dané úrovně (level).
        /// Vrácená šířka má hodnotu 5 - 75, default = 15.
        /// Tuto souřadnici neovlivňuje měřítko zobrazení: šířky v pásu Bands jsou konstantní bez ohledu na měřítko.
        /// </summary>
        /// <param name="level">Level Bandu, Root má level = 0. Z technických a bezpečnostních důvodů může mít level hodnotu 0 až 15, složitější sestavu si těžko představíme.</param>
        /// <returns></returns>
        internal float GetBandHeaderVisualWidth(int level)
        {
            int i = CheckBandLevel(level);
            float w = this.EditorGroupHeaderWidthList[i];
            return CheckBandHeaderWidth(w, w);
        }
        /// <summary>
        /// Metoda vrátí seznam komponent, které se v dané chvíli hlásí do daného Bandu (ať už jako Top nebo Bottom).
        /// </summary>
        /// <param name="bandId"></param>
        /// <returns></returns>
        internal List<ReportComponent> GetEditorComponentsForBand(int bandId)
        {
            List<ReportComponent> result;
            if (this.EditorInnerComponentDict != null && this.EditorInnerComponentDict.TryGetValue(bandId, out result))
                return result;
            return null;
        }
        /// <summary>
        /// Metoda najde a vrátí objekt záhlaví (ReportBand), který v danou chvíli je zobrazen na daném pixelu.
        /// </summary>
        /// <param name="visualPoint"></param>
        /// <returns></returns>
        internal ReportBand FindBandOnVisualPoint(PointF visualPoint)
        {
            foreach (ReportBand band in this.BandDict.Values)
            {
                if (band.CurrentRowVisualBound.HasValue)
                {
                    if (band.CurrentRowVisualBound.Value.Contains(visualPoint))
                        return band;
                }
            }
            foreach (ReportBand band in this.BandDict.Values)
            {
                if (band.CurrentRowVisualBound.HasValue)
                {
                    if (visualPoint.Y >= band.CurrentRowVisualBound.Value.Y && visualPoint.Y < band.CurrentRowVisualBound.Value.Bottom)
                        return band;
                }
            }
            return null;
        }
        /// <summary>
        /// Nastaví vizuální šířku (v pixelech) pro Band dané úrovně (level).
        /// Tuto souřadnici neovlivňuje měřítko zobrazení: šířky v pásu Bands jsou konstantní bez ohledu na měřítko.
        /// </summary>
        /// <param name="level">Level Bandu, Root má level = 0. Z technických a bezpečnostních důvodů může mít level hodnotu 0 až 15, složitější sestavu si těžko představíme.</param>
        /// <param name="width">Šířka bands, zarovnává se do mezí 5 až 75.</param>
        internal void SetBandHeaderVisualWidth(int level, float width)
        {
            int i = CheckBandLevel(level);
            float w = CheckBandHeaderWidth(width, width);
            this.EditorGroupHeaderWidthList[i] = w;
        }
        /// <summary>
        /// Vrátí číslo Bands.Level, v rozsahu 0-15. Současně zajistí, že v poli this.BandVisualWidthList bude přiměřený počet prvků.
        /// </summary>
        /// <param name="level"></param>
        /// <returns></returns>
        protected int CheckBandLevel(int level)
        {
            int l = (level < 0 ? 0 : (level > 15 ? 15 : level));
            if (this.EditorGroupHeaderWidthList == null)
                this.EditorGroupHeaderWidthList = new List<float>();
            while (this.EditorGroupHeaderWidthList.Count <= l)
                this.EditorGroupHeaderWidthList.Add(this.EditorGroupHeaderWidth);
            return l;
        }
        /// <summary>
        /// Vrátí danou šířku Bands v pixelech zarovnanou do mezí 12f - 95f
        /// </summary>
        /// <param name="widthRequired">Šířka požadovaná, na tu by se chtěl uživatel dostat</param>
        /// <param name="widthBefore">Šířka předchozí, na ní uživatel byl dosud</param>
        /// <returns></returns>
        internal float CheckBandHeaderWidth(float widthRequired, float widthBefore)
        {
            return (widthRequired < 17f ? 17f : (widthRequired > 75f ? 75f : widthRequired));
        }
        #endregion
        #region Public property svázané s this.Properties
        /// <summary>
        /// Šířka záhlaví v editoru, v pixelech
        /// </summary>
        internal float EditorBandHeaderWidth { get { return this.Properties.EditorBandHeaderWidth; } set { this.Properties.EditorBandHeaderWidth = value; } }
        /// <summary>
        /// Výška pravítka v editoru, v pixelech
        /// </summary>
        internal float EditorHRulerHeight { get { return this.Properties.EditorHRulerHeight; } set { this.Properties.EditorHRulerHeight = value; } }
        /// <summary>
        /// Šířka panelu vlastností v editoru, v pixelech
        /// </summary>
        internal float EditorPropertyPanelWidth { get { return this.Properties.EditorPropertyPanelWidth; } set { this.Properties.EditorPropertyPanelWidth = value; } }
        /// <summary>
        /// Výchozí šířka Bands
        /// </summary>
        protected float EditorGroupHeaderWidth { get { return this.Properties.EditorGroupHeaderWidth; } set { this.Properties.EditorGroupHeaderWidth = value; } }
        /// <summary>
        /// Souhrn šířek jednotlivých Bands, které jsou IsContainer.
        /// </summary>
        internal List<float> EditorGroupHeaderWidthList { get { return this.Properties.EditorGroupHeaderWidthList; } set { this.Properties.EditorGroupHeaderWidthList = value; } }
        /// <summary>
        /// Pro selectování výběrem: Poměrná část komponenty, která musí být ve Frame, aby komponenta byla selectována
        /// </summary>
        internal float SelectFrameRatio { get { return this.Properties.SelectFrameRatio; } set { this.Properties.SelectFrameRatio = value; } }
        #endregion
        #region Formulář, adapter na formulář
        /// <summary>
        /// Příznak, že Document má svůj Form. pak lze používat vizuální property.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        internal bool HasForm
        {
            get { return (this._EditorFormWr != null && this._EditorFormWr.IsAlive && this._EditorFormWr.Target != null && this._EditorFormWr.Target is EditorForm); }
        }
        /// <summary>
        /// Formulář, kde se provádí editace.
        /// Ukládá se WeakReference.
        /// Existenci lze ověřit přes property HasForm.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        internal EditorForm Form
        {
            get
            {
                if (this.HasForm)
                    return this._EditorFormWr.Target as EditorForm;
                return null;
            }
            set
            {
                if (this.HasForm)
                    this._FormUnbind(this.Form);

                if (value == null)
                    this._EditorFormWr = null;
                else
                {
                    this._EditorFormWr = new WeakReference(value);
                    this._FormBind(value);
                }
            }
        }
        /// <summary>
        /// Naváže své eventy do objektů předaného Formu
        /// </summary>
        /// <param name="editorForm"></param>
        private void _FormBind(EditorForm editorForm)
        {
            if (editorForm != null)
            {
                this.ComponentComboFill();
                this.UIRefresh();
            }
        }
        /// <summary>
        /// Odváže své eventy z objektů předaného Formu
        /// </summary>
        /// <param name="editorForm"></param>
        private void _FormUnbind(EditorForm editorForm)
        {
            if (editorForm != null)
            { }
        }
        
        
        
        /// <summary>
        /// Reference na objekt Editor.
        /// Je platná pouze pokud this.HasForm je true.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        internal EditorControl EditorControl
        {
            get { return (this.HasForm ? this.Form.ReportEditor : null); }
        }




        /// <summary>
        /// Fyzická WeakReference na formulář
        /// </summary>
        private WeakReference _EditorFormWr;
        #endregion
        #region UserInterface (Commandy) : řešení požadavků na provedení příkazů. Přicházejí z UI (menu, toolbar, panel vlastností) i z editoru (změna pozice, rozměru). Některé řeší dokument sám, některé předává do editoru.
        /// <summary>
        /// Metoda, která pomáhá řešit klávesové eventy.
        /// Typický postup: Control, který má focus, dostává od Windows fyzické klávesové eventy.
        /// Aby je nemusel dekódovat sám (protože jejich klávesové zkratky mohou být konfigurovatelné !), využívá tuto metodu UI = předá ji informace o klávesách.
        /// Tato metoda zjistí, zda klávesy odpovídají některému commandu, pokud ano tak tento command provede (vytvoří Command a zavolá standardní event CommandExecute), a vrací se true = byl to příkaz.
        /// Pokud to není příkaz, pak se event nevolá a vrací se false (klávesa je nevyřešena, control si ji může řešit sám).
        /// </summary>
        /// <param name="keyModifiers"></param>
        /// <param name="keyCode"></param>
        /// <returns></returns>
        internal bool UIKeyEventResolve(Keys keyModifiers, Keys keyCode)
        {
            if (this.HasForm)
                return this.Form.UIKeyCodeResolve(keyModifiers, keyCode, true);
            return false;
        }
        /// <summary>
        /// Zajistí aktualizaci vzhledu UI (enabled / disabled, checked, atd. na konkrétní položky UI)
        /// </summary>
        internal void UIRefresh()
        {
            if (!this.HasForm) return;

            List<EditorUIItemState> itemStateList = new List<EditorUIItemState>();

            List<ReportComponent> selectedComponents = this.SelectedComponents;
            int selectedCount = this.SelectedComponents.Count;

            this.UIRefreshDocument(itemStateList, selectedCount);
            this.UIRefreshClipboard(itemStateList, selectedCount);
            this.UIRefreshSelect(itemStateList, selectedCount, selectedComponents);
            this.UIRefreshRevert(itemStateList, selectedCount);
            this.UIRefreshObjects(itemStateList, selectedCount);

            this.Form.UIRefresh(itemStateList);
        }
        /// <summary>
        /// Provede specifikovanou akci.
        /// Akce je typicky vyvolaná aktivitou v menu / toolbaru.
        /// </summary>
        /// <param name="args"></param>
        internal void UIRunCommand(EditorCommandExecuteArgs args)
        {
            try
            {
                // Určí, zda command je interní (řeší se v rámci dokumentu). Vrací true = je vyřešeno.
                bool handled = this.RunCommandOnDocument(args);

                // pokud dokument nevyřešil command, dá ho k vyřešení do editoru na formulář:
                if (!handled && this.HasForm)
                    this.Form.EditorRunCommand(new EditorCommandRunArgs(args.CommandKey, args.Data));
            }
            catch (Exception exc)
            {
                Assistant.ShowError("Příkaz %0 nebylo možno provést, došlo k chybě: %1.", args.CommandKey, exc.Message);
            }
        }
        /// <summary>
        /// Pokusí se provést příkaz, pokud je interní.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        private bool RunCommandOnDocument(EditorCommandExecuteArgs args)
        {
            switch (args.CommandKey.CommandType)
            {
                case EditorCommandType.Document:
                    this.CmdDocument(args);
                    return true;
                case EditorCommandType.Edit:
                    this.CmdEdit(args);
                    return true;
                case EditorCommandType.Align:
                    this.CmdAlign(args);
                    return true;
                case EditorCommandType.Equal:
                    this.CmdEqual(args);
                    return true;
                case EditorCommandType.ZOrder:
                    this.CmdZOrder(args);
                    return true;
                // Commandy typu Zoom, ObjectSelect, ObjectInsert neřeším zde, ale předávám je do editoru, viz výše.
            }
            return false;
        }
        #endregion
        #region Podpora PropertyGridu
        /// <summary>
        /// Reakce na událost na Formu, kdy uživatel vstoupil (myší, focus) do PropertyGridu.
        /// Nyní lze očekávat, že dojde ke změně hodnot u aktuálně vybraných položek.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void PropertyPanelEnter()
        {
            if (this.ItemsInPropertyGrid != null)
                this.Reverter.StoreChangeBefore(this.ItemsInPropertyGrid);
        }
        /// <summary>
        /// Reakce na událost na Formu, kdy uživatel změnil nějakou hodnotu v PropertyGridu.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void PropertyGridValueChanged(PropertyValueChangedEventArgs e)
        {
            if (this.ItemsInPropertyGrid != null)
            {
                this.Reverter.StoreChangeAfter(this.ItemsInPropertyGrid);
                this.Reverter.StoreChangeBefore(this.ItemsInPropertyGrid);
            }
            this.UIRefresh();
        }
        /// <summary>
        /// Reakce na událost na Formu, kdy uživatel změnil vybraný objekt v combo nad PropertyGridem.
        /// </summary>
        public void PropertyComboChanged()
        {
            if (this._SuppressEventChangePropertyCombo) return;        // Potlačený event

            DComboItem selectedDItem = (this.HasForm ? this.Form.PropertyComboSelectedItem : null);
            if (selectedDItem == null) return;
            int componentId = (int)selectedDItem.ID;
            ReportComponent component = this.GetComponent(componentId);
            this.SelectComponentFromCombo(component);
            this.ReDraw();
        }



        /// <summary>
        /// Vyprázdní obsah PropertyGridu
        /// </summary>
        protected void ClearPropertyGrid()
        {
            this.ShowInPropertyGrid((IRevertItem)null);
        }
        /// <summary>
        /// Zajistí předání dodané položky do PropertyGridu.
        /// Položka je současně uložena do seznamu this.LastItemsPreparedToRevert, ale Reverter není aktivován.
        /// </summary>
        /// <param name="item"></param>
        protected void ShowInPropertyGrid(IRevertItem item)
        {
            this.ShowInPropertyGrid(new IRevertItem[] { item });
        }
        /// <summary>
        /// Zajistí předání položek z dodaného seznamu do PropertyGridu.
        /// Položky jsou současně uloženy do seznamu this.LastItemsPreparedToRevert, ale Reverter není aktivován.
        /// </summary>
        /// <param name="list"></param>
        protected void ShowInPropertyGrid(IEnumerable<IRevertItem> list)
        {
            this.ItemsInPropertyGrid = list.ToList();
            if (this.HasForm)
                this.Form.PropertyPanelSelectedObjects = this.GetObjectForPropertyGrid(list);
        }
        /// <summary>
        /// Metoda vrátí souhrn objektů, které se budou zobrazovat v PropertyGridu.
        /// Účel: z dodaných objektů, pokud jsou ReportComponent, do výstupu vrací jejich ReportObject = vnitřní výkonný objekt (s jeho reálnými reportovými vlastnostmi).
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        protected object[] GetObjectForPropertyGrid(IEnumerable<IRevertItem> list)
        {
            List<object> result = new List<object>();
            foreach (IRevertItem item in list)
            {
                if (item == null) continue;
                if (item is ReportComponent)
                    result.Add((item as ReportComponent).ReportObject);
                else
                    result.Add(item);
            }
            return result.ToArray<object>();
        }
        /// <summary>
        /// Metoda se volá po každé změně počtu a výběru komponent.
        /// Aktuální stav promítne do property gridu, do jeho pole Combo, kde se nabízejí objekty sestavy.
        /// </summary>
        protected void ComponentsChanged()
        {
            this.ComponentsChanged(false);
        }
        /// <summary>
        /// Metoda se volá po každé změně počtu a výběru komponent.
        /// Aktuální stav promítne do property gridu, do jeho pole Combo, kde se nabízejí objekty sestavy.
        /// </summary>
        protected void ComponentsChanged(bool force)
        {
            if (force || this.ComponentDict.Count != this._ComponentCountLastInPropertyCombo)
                this.ComponentComboFill();
        }
        /// <summary>
        /// Naplní do PropertyGrid.ComboObject všechny komponenty editoru
        /// </summary>
        protected void ComponentComboFill()
        {
            if (!this.HasForm) return;

            DComboItem selectedDItem = (this.HasForm ? this.Form.PropertyComboSelectedItem : null);
            IComparable selectId = (selectedDItem == null ? -1 : selectedDItem.ID);

            List<DComboItem> components = new List<DComboItem>();
            selectedDItem = null;
            foreach (ReportComponent component in this.ComponentDict.Values)
            {
                string name = component.ReportObject.ComponentName;
                if (!String.IsNullOrEmpty(name))
                {
                    DComboItem item = new DComboItem(component.ComponentId, name, component);
                    components.Add(item);
                    if (item.ID.CompareTo(selectId) == 0)
                        selectedDItem = item;
                }
            }
            this.Form.PropertyComboItems = components;

            if (selectedDItem != null)
                this.ComponentComboSelectItem(selectedDItem, true);

            this._ComponentCountLastInPropertyCombo = this.ComponentDict.Count;
        }
        /// <summary>
        /// Do Property panelu do combo boxu objektů aktivuje daný objekt. Volitelně potlačí event OnChange.
        /// </summary>
        /// <param name="selectID"></param>
        /// <param name="suppressChange"></param>
        protected void ComponentComboSelectItem(IComparable selectID, bool suppressChange)
        {
            if (this.HasForm)
            {
                DComboItem selectedDItem = this.Form.PropertyComboFindItem(selectID);
                this.ComponentComboSelectItem(selectedDItem, suppressChange);
            }
        }
        /// <summary>
        /// Do Property panelu do combo boxu objektů aktivuje daný objekt. Volitelně potlačí event OnChange.
        /// </summary>
        /// <param name="selectedDItem"></param>
        /// <param name="suppressChange"></param>
        private void ComponentComboSelectItem(DComboItem selectedDItem, bool suppressChange)
        {
            if (!this.HasForm) return;

            bool currentSuppress = this._SuppressEventChangePropertyCombo;
            using (Assistant.DisposableScope(delegate() { this._SuppressEventChangePropertyCombo = currentSuppress; }))
            {
                if (suppressChange)
                    this._SuppressEventChangePropertyCombo = true;
                this.Form.PropertyComboSelectedItem = selectedDItem;
            }
        }
        /// <summary>
        /// Počet komponent při posledním plnění combo objektů.
        /// </summary>
        private int _ComponentCountLastInPropertyCombo = 0;
        /// <summary>
        /// Potlačení provedení eventu po změně vybrané položky v ObjectCombo.
        /// </summary>
        private bool _SuppressEventChangePropertyCombo;
        #endregion
        #region Vkládání nových objektů v editoru
        /// <summary>
        /// Akce, kdy editor vkládá novou komponentu do reportu.
        /// Fyzickou práci zajišťuje dokument.
        /// </summary>
        /// <param name="band"></param>
        /// <param name="commandKey"></param>
        /// <param name="visualLocation"></param>
        /// <returns></returns>
        internal ReportComponent EditorInsertComponentCreate(ReportBand band, EditorCommandKey commandKey, PointF visualLocation)
        {
            Type objectType;
            if (commandKey.TryGetSpecification<Type>(out objectType) && objectType != null)
            {
                ReportComponent component = ReportComponent.Create(this, objectType);
                if (component != null)
                {
                    component.OwnerTopBandId = band.BandId;
                    this._AddComponent(component, true);
                    return component;
                }
            }
            return null;
        }
        /// <summary>
        /// Akce, kdy editor dokončil vkládání nové komponenty.
        /// </summary>
        /// <param name="component"></param>
        internal void EditorInsertComponentDone(ReportComponent component)
        {
            this.Reverter.StoreAdd(component);
            this.UIRefresh();
        }
        /// <summary>
        /// Refresh UI za prvky typu Object, což je svázáno jak s aktuální sadou commandů, tak se stavem editoru.
        /// </summary>
        /// <param name="itemStateList"></param>
        /// <param name="selectedCount"></param>
        private void UIRefreshObjects(List<EditorUIItemState> itemStateList, int selectedCount)
        {
            if (!this.HasForm) return;

            bool enableEditor = (this.Form.EditorContent == EditorForm.EditorContentType.Editor);
            bool isSelectMode = (this.EditorControl.ObjectState == EditorObjectState.Select);       // true = je režim Select
            bool isAnchorMode = (this.EditorControl.ObjectState == EditorObjectState.Anchor);       // true = je režim Anchor
            bool isInsertLock = this.EditorControl.IsLockedInsertObjectMode;                        // true = režim Insert je zamčený

            // itemStateList.Add(new EditorUIItemState(EditorCommandType.ObjectInsert, null) { Enabled = true, Checked = !editorIsInInsertObjectMode });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Object, EditorCommandObjectType.ObjectSelect) { Enabled = enableEditor, Checked = isSelectMode });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Object, EditorCommandObjectType.ObjectAnchor) { Enabled = enableEditor, Checked = isAnchorMode });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Object, EditorCommandObjectType.ObjectLockInsert) { Enabled = enableEditor, Checked = isInsertLock });

            IEnumerable<EditorCommand> commands = this.Form.UIFindCommands(cmd => cmd.CommandType == EditorCommandType.ObjectInsert);   // Všechny commandy typu ObjectInsert
            EditorCommandKey editorCommandKey = this.EditorControl.CurrentObjectCommandKey;        // Aktuální commandKey určuje typ objektu pro Insert (pokud jde o command ObjectInsert)
            foreach (EditorCommand command in commands)
            {
                itemStateList.Add(new EditorUIItemState(command.CommandKey) { Enabled = enableEditor, Checked = (command.CommandKey == editorCommandKey) });
            }
        }
        #endregion
        #region Interaktivní pohyby komponent, vycházejíci z Editoru
        /// <summary>
        /// Zahajuje se pohyb komponent.
        /// Aktivně se pohybuje/resizuje daná komponenta, volitelně se má změna týkat i všech komponent aktuálně selectovaných.
        /// Připraví data pro Reverter.
        /// </summary>
        /// <param name="reportComponent"></param>
        /// <param name="args"></param>
        /// <param name="moveSelected"></param>
        internal void DragComponentsBegin(ReportComponent reportComponent, EditInteractiveEventArgs args, bool moveSelected)
        {
            List<ReportComponent> list = new List<ReportComponent>();
            if (moveSelected)
                list.AddRange(this.SelectedComponents);

            bool isSelected = (reportComponent != null && list.Any(c => ReportComponent.ReferenceEquals(c, reportComponent)));
            if (reportComponent != null && !isSelected)
                list.Add(reportComponent);

            this.DragComponentAllList = list;
            this.DragComponentOtherList = list.Where(c => !ReportComponent.ReferenceEquals(c, reportComponent)).ToList();
            this.DragComponentActive = reportComponent;
            this.DragComponentIsSelected = isSelected;

            foreach (ReportComponent other in this.DragComponentOtherList)
                other.OnOtherDragComponentsBegin(reportComponent, args);
        }
        /// <summary>
        /// Provádí jednotlivý pohyb komponent.
        /// Aktivně se pohybuje/resizuje daná komponenta, změna se má týkat všech dříve připravených komponent (v metodě this.DragComponentsBegin())
        /// </summary>
        /// <param name="reportComponent"></param>
        /// <param name="args"></param>
        internal void DragComponentsStep(ReportComponent reportComponent, EditInteractiveEventArgs args)
        {
            foreach (ReportComponent other in this.DragComponentOtherList)
                other.OnOtherDragComponentsStep(reportComponent, args);
        }
        /// <summary>
        /// Dokončení Drag, před provedením změn
        /// </summary>
        /// <param name="reportComponent"></param>
        /// <param name="args"></param>
        internal void DragComponentsDoneBefore(ReportComponent reportComponent, EditInteractiveEventArgs args)
        {
            this.Reverter.StoreChangeBefore(this.DragComponentAllList);
        }
        /// <summary>
        /// Ukončí (potvrdí) jednotlivý pohyb komponent.
        /// Na závěr zapíše do Reverteru aktuální data.
        /// </summary>
        /// <param name="reportComponent"></param>
        /// <param name="args"></param>
        internal void DragComponentsDone(ReportComponent reportComponent, EditInteractiveEventArgs args)
        {
            foreach (ReportComponent other in this.DragComponentOtherList)
                other.OnOtherDragComponentsDone(reportComponent, args);
        }
        /// <summary>
        /// Dokončení Drag, po provedení změn
        /// </summary>
        /// <param name="reportComponent"></param>
        /// <param name="args"></param>
        internal void DragComponentsDoneAfter(ReportComponent reportComponent, EditInteractiveEventArgs args)
        {
            this.Reverter.StoreChangeAfter(this.DragComponentAllList);
            this.UIRefresh();

            this.DragComponentAllList = null;
            this.DragComponentOtherList = null;
            this.DragComponentActive = null;
            this.DragComponentIsSelected = false;
        }
        /// <summary>
        /// Soupis všech komponent, které se účastní přesouvání = komponenty Selected + komponenta aktivní.
        /// Tento seznam se používá pro komunikaci s Reverterem, sděluje mi souhrn změn.
        /// </summary>
        protected List<ReportComponent> DragComponentAllList { get; set; }
        /// <summary>
        /// Soupis všech komponent, které se účastní přesouvání, kromě komponenty aktivní = komponenty Selected - komponenta aktivní.
        /// Aktivní komponenta může nebo nemusí být selectovaná.
        /// Jde o komponenty, které se pohybují návazně ke komponentě aktivní.
        /// </summary>
        protected List<ReportComponent> DragComponentOtherList { get; set; }
        /// <summary>
        /// Komponenta aktivní = s tou uživatel hýbe. Může nebo nemusí být selectovaná, viz this.DragComponentIsSelected
        /// </summary>
        protected ReportComponent DragComponentActive { get; set; }
        /// <summary>
        /// Příznak, že aktivní komponenta (this.DragComponentActive) je současně selectovaná.
        /// </summary>
        protected bool DragComponentIsSelected { get; set; }
        #endregion
        #region Podpora Reverteru
        /// <summary>
        /// Metoda připraví dané objekty do Reverteru a následně je zobrazí v PropertyGridu.
        /// </summary>
        /// <param name="iRevert"></param>
        internal void PrepareToRevertAndPropertyGrid(IRevertItem iRevert)
        {
            if (iRevert == null) return;

            // this.PrepareToRevertAndPropertyGrid(new List<IRevertItem>() { iRevert });
        }
        /// <summary>
        /// Metoda připraví dané objekty do Reverteru a následně je zobrazí v PropertyGridu.
        /// </summary>
        /// <param name="iRevertItems"></param>
        internal void PrepareToRevertAndPropertyGrid(IEnumerable<IRevertItem> iRevertItems)
        {
            if (iRevertItems == null) return;

            this.ItemsInPropertyGrid = iRevertItems.ToList();

            this.ShowInPropertyGrid(this.ItemsInPropertyGrid);
        }
        /// <summary>
        /// Provede akci UNDO
        /// </summary>
        private void CmdRevertUndo()
        {
            if (this.Reverter.CanUndo)
            {
                this.Reverter.Undo();
                this.ReDraw();
                this.UIRefresh();
            }
        }
        /// <summary>
        /// Provede akci REDO
        /// </summary>
        private void CmdRevertRedo()
        {
            if (this.Reverter.CanRedo)
            {
                this.Reverter.Redo();
                this.ReDraw();
                this.UIRefresh();
            }
        }
        /// <summary>
        /// Metoda přidá svůj díl informací pro Refresh UI, a to za prvky UI: Undo, Redo
        /// </summary>
        /// <param name="itemStateList">Pole informací o položkách UI a jejich aktuálním stavu</param>
        /// <param name="selectedCount">Počet vybraných prvků</param>
        private void UIRefreshRevert(List<EditorUIItemState> itemStateList, int selectedCount)
        {
            bool enableEditor = (this.Form.EditorContent == EditorForm.EditorContentType.Editor);

            itemStateList.Add(new EditorUIItemState(EditorCommandType.Edit, EditorCommandEditType.EditUndo) { Enabled = this.Reverter.CanUndo && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Edit, EditorCommandEditType.EditRedo) { Enabled = this.Reverter.CanRedo && enableEditor });
        }
        /// <summary>
        /// Soupis objektů (typu IRevertItem), které jsou zobrazeny v PropertyGridu, a jsou připraveny pro předání do Reverteru.
        /// V případě Components jsou zde uloženy Components, a ne jejich ReportObject (na rozdíl od metody GetObjectForPropertyGrid(), která z Components vybírá a vrací právě jejich ReportObject).
        /// </summary>
        protected List<IRevertItem> ItemsInPropertyGrid;
        #region IRevertContainer Members : metody, které používá Reverter v situaci, kdy přidává / vyhledává / odebírá některý prvek v rámci akcí Undo / Redo
        IRevertItem IRevertContainer.CreateItem(IComparable objectId, string objectData)
        {
            ItemId itemId = objectId as ItemId;
            if (itemId == null) return null;

            switch (itemId.Type)
            {
                case ItemType.Band:
                    ReportBand band = ReportBand.XmlDeserializeFrom(XElement.Parse(objectData), true);
                    if (band == null) return null;
                    this.AddBand(band);
                    return band;
                case ItemType.Component:
                    ReportComponent component = ReportComponent.XmlDeserializeFrom(this, XElement.Parse(objectData), true);
                    if (component == null) return null;
                    this.AddComponent(component);
                    return component;
            }
            return null;
        }
        IRevertItem IRevertContainer.GetItem(IComparable objectId)
        {
            ItemId itemId = objectId as ItemId;
            if (itemId == null) return null;

            switch (itemId.Type)
            {
                case ItemType.Band:
                    return this.GetBand(itemId.Id);
                case ItemType.Component:
                    return this.GetComponent(itemId.Id);
            }
            return null;
        }
        void IRevertContainer.RemoveItem(IComparable objectId)
        {
            ItemId itemId = objectId as ItemId;
            if (itemId == null) return;

            switch (itemId.Type)
            {
                case ItemType.Band:
                    this.RemoveBand(itemId.Id);
                    break;
                case ItemType.Component:
                    this.RemoveComponent(itemId.Id);
                    break;
            }
        }
        #endregion
        #endregion
        #region Copy, Cut, Paste, Delete, Undo / Redo
        /// <summary>
        /// Řešení akcí z UI typu Edit.
        /// </summary>
        /// <param name="args"></param>
        private void CmdEdit(EditorCommandExecuteArgs args)
        {
            EditorCommandEditType action;
            if (!args.CommandKey.TryGetSpecification<EditorCommandEditType>(out action))
                return;

            switch (action)
            {
                case EditorCommandEditType.EditCopy:
                    this.CmdEditCopy();
                    break;
                case EditorCommandEditType.EditCut:
                    this.CmdEditCut();
                    break;
                case EditorCommandEditType.EditPaste:
                    this.CmdEditPaste();
                    break;
                case EditorCommandEditType.EditDelete:
                    this.CmdEditDelete();
                    break;
                case EditorCommandEditType.EditUndo:
                    this.CmdRevertUndo();
                    break;
                case EditorCommandEditType.EditRedo:
                    this.CmdRevertRedo();
                    break;
            }
        }
        /// <summary>
        /// Zajistí zkopírování vybraných komponent do clipboardu (toto je beze změny v Reverteru).
        /// Pozor: musí předpokládat, že zdrojový report (this) může být fyzicky jiný, než cílový report, kam bude probíhat Paste.
        /// </summary>
        private void CmdEditCopy()
        {
            List<ReportComponent> selectedComponents = this.SelectedComponents;
            if (this.SelectedComponents.Count == 0) return;
            this.CmdClipboardStore(selectedComponents);
            this.UIRefresh();
        }
        /// <summary>
        /// Zajistí přenesení vybraných komponent do clipboardu a jejich smazání, včetně zápisu typu Remove do Reverteru, refreshe UI a ReDraw editoru.
        /// Pozor: musí předpokládat, že zdrojový report (this) může být fyzicky jiný, než cílový report, kam bude probíhat Paste.
        /// </summary>
        private void CmdEditCut()
        {
            List<ReportComponent> selectedComponents = this.SelectedComponents;
            if (this.SelectedComponents.Count == 0) return;
            this.CmdClipboardStore(selectedComponents);
            this.CmdEditDelete(this.SelectedComponents);
            this.UIRefresh();
        }
        /// <summary>
        /// Zajistí načtení dat z Clipboardu (pokud jsou naše), a jejich vložení do reportu.
        /// Pozor: musí předpokládat, že zdrojový report (kde proběhlo Copy/Cut) může být fyzicky jiný, než cílový report (this), kde se provádí Paste.
        /// </summary>
        private void CmdEditPaste()
        {
            // Načtu komponenty z clipboardu:
            List<ReportComponent> pasteComponents = this.CmdClipboardRead();
            if (pasteComponents.Count == 0) return;

            // Načtené komponenty zařadíme do this struktury, mírně je posuneme doprava dolů (aby byly vidět poté, kdy někdo dá Copy + Paste), zajistíme jejich selectování:
            this.SelectComponentClear();
            foreach (ReportComponent component in pasteComponents)
            {
                ReportBand band;
                if (!this.TryGetBand(component.OwnerTopBandId, out band))
                    return;

                component.RelativeBounds.ShiftBy(new SizeF(3f, 3f));
                component.IsSelected = true;

                this.AddComponent(component);
            }

            this.Reverter.StoreAdd(pasteComponents);
            this.UIRefresh();
            this.ReDraw();
        }
        /// <summary>
        /// Zajistí smazání vybraných komponent, včetně zápisu typu Remove do Reverteru
        /// </summary>
        private void CmdEditDelete()
        {
            this.CmdEditDelete(this.SelectedComponents);
        }
        /// <summary>
        /// Zajistí smazání vybraných komponent, včetně zápisu typu Remove do Reverteru, refreshe UI a ReDraw editoru
        /// </summary>
        private void CmdEditDelete(List<ReportComponent> deleteComponents)
        {
            if (deleteComponents.Count == 0) return;

            this.Reverter.StoreRemove(deleteComponents);
            foreach (ReportComponent component in deleteComponents)
                this.RemoveComponent(component.ComponentId);

            this.SelectComponentRemove(deleteComponents);
            this.ShowInPropertyGrid(this.SelectedComponents);

            this.UIRefresh();
            this.ReDraw();
        }
        /// <summary>
        /// Z vybraných komponent sestaví XML text a ten umístí do Clipboardu
        /// </summary>
        /// <param name="selectedComponents"></param>
        private void CmdClipboardStore(IEnumerable<ReportComponent> selectedComponents)
        {
            // XML dokument:
            XElement xReport = new XElement(XML_REPORT);
            xReport.Add(new XAttribute("Version", "1.00"));
            xReport.Add(new XAttribute("Content", "SelectedComponents"));
            XDocument xdoc = new XDocument(xReport);

            // Komponenty:
            XElement xComponents = new XElement(XML_COMPONENTS);
            foreach (ReportComponent comp in selectedComponents)
            {
                XElement xComponent = new XElement(XML_COMPONENT);
                comp.XmlSerializeTo(xComponent, true);
                xComponents.Add(xComponent);
            }
            xdoc.Root.Add(xComponents);

            string xmlData = xdoc.ToString();
            System.Windows.Forms.Clipboard.SetData(System.Windows.Forms.DataFormats.Html, xmlData);
        }
        /// <summary>
        /// Pokusí se načíst data ze schránky, konvertovat data na komponenty, a vrátit je ve formě Listu
        /// </summary>
        /// <returns></returns>
        private List<ReportComponent> CmdClipboardRead()
        {
            List<ReportComponent> pasteComponents = new List<ReportComponent>();

            try
            {
                if (!System.Windows.Forms.Clipboard.ContainsData(System.Windows.Forms.DataFormats.Html)) return pasteComponents;
                object data = System.Windows.Forms.Clipboard.GetData(System.Windows.Forms.DataFormats.Html);
                if (data == null || !(data is string)) return pasteComponents;
                string xmlData = data as string;

                XElement root = XElement.Parse(xmlData);
                if (root.HasElements)
                {
                    foreach (XElement elem in root.Elements())
                    {   // Root obsahuje elementy například XML_PROPERTIES, XML_GRID, XML_BANDS, XML_COMPONENTS
                        string name = elem.Name.LocalName;
                        if (name == XML_COMPONENTS)
                        {   // XML_COMPONENTS obsahuje sadu elementů jména XML_COMPONENT:
                            foreach (XElement comp in elem.Elements())
                            {
                                if (comp.Name.LocalName == XML_COMPONENT)
                                {
                                    ReportComponent component = ReportComponent.XmlDeserializeFrom(this, comp, false);
                                    if (component != null)
                                        pasteComponents.Add(component);
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception exc)
            {
                // Steward.Diagnostic.InfoAddLineWithTime("Load components from Clipboard, exception: " + exc.Message);
            }
            return pasteComponents;
        }
        /// <summary>
        /// Metoda přidá svůj díl informací pro Refresh UI, a to za prvky UI které jsou ovlivněné stavem Clipboardu
        /// </summary>
        /// <param name="itemStateList">Pole informací o položkách UI a jejich aktuálním stavu</param>
        /// <param name="selectedCount">Počet vybraných prvků</param>
        private void UIRefreshClipboard(List<EditorUIItemState> itemStateList, int selectedCount)
        {
            bool enableEditor = (this.Form.EditorContent == EditorForm.EditorContentType.Editor);
            bool isAtLeastOne = (selectedCount >= 1);
            bool isInClipboard = UITestClipboardContent();

            itemStateList.Add(new EditorUIItemState(EditorCommandType.Edit, EditorCommandEditType.EditCopy) { Enabled = isAtLeastOne && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Edit, EditorCommandEditType.EditCut) { Enabled = isAtLeastOne && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Edit, EditorCommandEditType.EditPaste) { Enabled = isInClipboard && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Edit, EditorCommandEditType.EditDelete) { Enabled = isAtLeastOne && enableEditor });
        }
        /// <summary>
        /// Vrací true, pokud se zdá, že Clipboard obsahuje smysluplný obsah z hlediska Paste do reportu
        /// </summary>
        /// <returns></returns>
        private bool UITestClipboardContent()
        {
            if (!System.Windows.Forms.Clipboard.ContainsData(System.Windows.Forms.DataFormats.Html)) return false;
            object data = System.Windows.Forms.Clipboard.GetData(System.Windows.Forms.DataFormats.Html);
            if (data == null || !(data is string)) return false;
            string xmlData = data as string;
            if (String.IsNullOrEmpty(xmlData)) return false;

            // Následuje pouze formální kontrola, neprovádím kompletní parsování a rekonstrukci:
            bool begin = (xmlData.StartsWith("<report Version=\"1.00\" Content=\"SelectedComponents\">") || xmlData.StartsWith("<report Version=\"2.00\" Content=\"SelectedComponents\">"));
            if (!begin) return false;
            bool comp = (xmlData.Substring(0, 150).Contains("<components>"));
            if (!comp) return false;

            return true;

            /*
<report Version="1.00" Content="SelectedComponents">
  <components>
    <component Type="ReportObjectShape" ComponentId="1" BandId="5" BandIdName="Report.Detail Row" RelativeBounds="0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00" Bounds="10.00;2.00;160.00;7.00" BorderColor="Black" BorderWidth="0.50" BackColor="ControlDark" Shape="Rectangle" ShapeEdge="0.00" />
  </components>
</report>
            */
        }
        #endregion
        #region Align, Equal, ZOrder
        /// <summary>
        /// Řešení akcí z UI typu Align.
        /// Zajistí zarovnání aktuálně vybraných komponent podle daného příkazu
        /// </summary>
        /// <param name="args"></param>
        private void CmdAlign(EditorCommandExecuteArgs args)
        {
            EditorCommandAlignType action;
            if (!args.CommandKey.TryGetSpecification<EditorCommandAlignType>(out action))
                return;

            List<ReportComponent> selectedComponents = this.SelectedComponents;
            this.Reverter.StoreChangeBefore(selectedComponents);

            RectangleF summaryArea = DrawingExtensions.SummaryRectangle(selectedComponents.ConvertAll(c => c.CurrentVisualArea));
            foreach (ReportComponent component in selectedComponents)
            {
                RectangleF newVisualArea = this._CmdAlign(component.CurrentVisualArea, action, summaryArea);
                component.SetVisualBound(newVisualArea);
            }

            this.Reverter.StoreChangeAfter(selectedComponents);
            this.UIRefresh();
            this.ReDraw();
        }
        /// <summary>
        /// Vrátí souřadnice daného rectangle, zarovnané daným stylem k danému sumárnímu rectangle.
        /// </summary>
        /// <param name="input">Vstupní souřadnice konkrétního prvku</param>
        /// <param name="align">Režim zarovnání</param>
        /// <param name="summaryArea">Souřadnice sumární</param>
        /// <returns></returns>
        private RectangleF _CmdAlign(RectangleF input, EditorCommandAlignType align, RectangleF summaryArea)
        {
            RectangleF result = input;
            switch (align)
            {
                case EditorCommandAlignType.AlignHLeft:
                    result.X = summaryArea.X;
                    break;
                case EditorCommandAlignType.AlignHCenter:
                    result.X = summaryArea.X + (summaryArea.Width - result.Width) / 2f;
                    break;
                case EditorCommandAlignType.AlignHRight:
                    result.X = summaryArea.X + summaryArea.Width - result.Width;
                    break;
                case EditorCommandAlignType.AlignVTop:
                    result.Y = summaryArea.Y;
                    break;
                case EditorCommandAlignType.AlignVMiddle:
                    result.Y = summaryArea.Y + (summaryArea.Height - result.Height) / 2f;
                    break;
                case EditorCommandAlignType.AlignVBottom:
                    result.Y = summaryArea.Y + summaryArea.Height - result.Height;
                    break;
            }
            return result;
        }
        /// <summary>
        /// Řešení akcí z UI typu Equal.
        /// Zajistí zarovnání aktuálně vybraných komponent podle daného příkazu
        /// </summary>
        /// <param name="args"></param>
        private void CmdEqual(EditorCommandExecuteArgs args)
        { }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="args"></param>
        private void CmdZOrder(EditorCommandExecuteArgs args)
        {
            EditorCommandZOrderType action;
            if (!args.CommandKey.TryGetSpecification<EditorCommandZOrderType>(out action))
                return;

            List<ReportComponent> allComponents = new List<ReportComponent>(this.ComponentDict.Values);
            this.Reverter.StoreChangeBefore(allComponents);
            allComponents.Sort(ReportComponent.CompareByZOrder);

            List<ReportComponent> selectedComponents = this.SelectedComponents;

            bool isChange = false;
            switch (action)
            {
                case EditorCommandZOrderType.ZOrderBottom:
                    isChange = this.CmdZOrderBottom(allComponents, selectedComponents);
                    break;
                case EditorCommandZOrderType.ZOrderLower:
                    isChange = this.CmdZOrderLower(allComponents, selectedComponents);
                    break;
                case EditorCommandZOrderType.ZOrderHigher:
                    isChange = this.CmdZOrderHigher(allComponents, selectedComponents);
                    break;
                case EditorCommandZOrderType.ZOrderTop:
                    isChange = this.CmdZOrderTop(allComponents, selectedComponents);
                    break;
            }

            if (isChange)
            {
                this.Reverter.StoreChangeAfter(allComponents);
                this.UIRefresh();
                this.ReDraw();
            }
        }

        private bool CmdZOrderBottom(List<ReportComponent> allComponents, List<ReportComponent> selectedComponents)
        {
            return false;
        }

        private bool CmdZOrderLower(List<ReportComponent> allComponents, List<ReportComponent> selectedComponents)
        {
            return false;
        }

        private bool CmdZOrderHigher(List<ReportComponent> allComponents, List<ReportComponent> selectedComponents)
        {
            return false;
        }

        private bool CmdZOrderTop(List<ReportComponent> allComponents, List<ReportComponent> selectedComponents)
        {
            return false;
        }

        #endregion
        #region Selectování
        #region Selectování Components
        /// <summary>
        /// Vybere / zruší výběr jedné dané komponenty.
        /// Pokud je zadáno withCtrl = true, provádí se select/deselect. 
        /// Pokud je zadáno withCtrl = false, nejprve se všechny nyní selectované komponenty odselectují a pouze zadaná se nastaví jako selectovaná.
        /// Tato metoda pracuje s property ReportComponent.IsSelected (jednotkový výběr), nikoli ReportComponent.IsSelectedFrame.
        /// Ale při odselectování všech tuto property ReportComponent.IsSelectedFrame dává na false.
        /// </summary>
        /// <param name="component">Komponenta</param>
        /// <param name="withCtrl">S klávesou CTRL = ponechat stávající výběr, aktuální komponentu selectovat XOR</param>
        /// <returns></returns>
        internal bool SelectComponentOne(ReportComponent component, bool withCtrl)
        {
            return this._SelectComponentOne(component, withCtrl, true);
        }
        /// <summary>
        /// Vybere / zruší výběr jedné dané komponenty.
        /// Volá se po výběru v Property panelu v Combo boxu.
        /// </summary>
        /// <param name="component">Komponenta</param>
        /// <returns></returns>
        internal bool SelectComponentFromCombo(ReportComponent component)
        {
            return this._SelectComponentOne(component, false, false);
        }
        /// <summary>
        /// Vybere / zruší výběr jedné dané komponenty.
        /// </summary>
        /// <param name="component">Komponenta</param>
        /// <param name="withCtrl">S klávesou CTRL = ponechat stávající výběr, aktuální komponentu selectovat XOR</param>
        /// <param name="selectInComboObjects">Zajistit zobrazení vybraných komponent v Property panelu v Combo boxu</param>
        /// <returns></returns>
        private bool _SelectComponentOne(ReportComponent component, bool withCtrl, bool selectInComboObjects)
        {
            if (component != null)
            {
                if (this.EditorComponents.Any(c => Object.ReferenceEquals(c, component)))
                {
                    if (withCtrl)
                        component.IsSelected = !component.Selected;
                    else
                    {
                        this.SelectComponentClear(true);
                        component.IsSelected = true;
                    }
                }
                this.SelectComponentsAfter(selectInComboObjects);
            }
            return false;
        }
        /// <summary>
        /// Výběr komponent pomocí framování - iniciace (při zahájení)
        /// </summary>
        /// <param name="clearAll"></param>
        internal void SelectComponentFrameInit(bool clearAll)
        {
            foreach (ReportComponent component in this.ComponentDict.Values)
            {
                component.IsSelectedFrame = false;
                if (clearAll)
                    component.IsSelected = false;
            }
            this.SelectComponentsAfter(false);
        }
        /// <summary>
        /// Výběr komponent pomocí framování - výběr položek odpovídajících rámci.
        /// Na vstupu jsou souřadnice vizuálního prostoru Frame, 
        /// odpovídající komponenty se určují rovněž podle jejich vizuálních souřadnic.
        /// </summary>
        /// <param name="visualArea"></param>
        /// <returns></returns>
        internal bool SelectComponentFrameAdd(RectangleF visualArea)
        {
            IEnumerable<ReportComponent> components = this.EditorComponents;

            // Nejprve všem komponentám zruším příznak IsSelectedFrame 
            //  Proč? Když provádím framování, označuji některé prvky. Když přitom ale zmenším frame a některé prvky mi vypadnou, musí být odselectovány.
            foreach (ReportComponent component in components.Where(c => c.IsSelectedFrame))
                component.IsSelectedFrame = false;

            foreach (ReportComponent component in components)
            {
                if (component.IsSelectedInVisualFrame(visualArea, this.SelectFrameRatio))
                    component.IsSelectedFrame = true;
            }
            // Nevolám: this.SelectComponentsAfter()    protože selectování aktuálně probíhá, a After zavolám až bude hotovo, viz this.SelectComponentFrameDone() !
            return true;
        }
        /// <summary>
        /// Výběr komponent pomocí framování - dokončení (komponenty označené rámcem označí jako selectované)
        /// </summary>
        internal void SelectComponentFrameDone()
        {
            IEnumerable<ReportComponent> components = this.EditorComponents;
            foreach (ReportComponent component in components)
            {
                if (component.IsSelectedFrame)
                {
                    if (!component.IsSelected)
                        component.IsSelected = true;
                    component.IsSelectedFrame = false;
                }
            }
            this.SelectComponentsAfter(true);
        }
        /// <summary>
        /// Ze seznamu Selected odebere prvky, které jsou v dodaném seznamu.
        /// Reálně provede to, že pro dané prvky zavolá jejich metodu SelectClear(),
        /// protože seznam selectovaných prvků this.SelectedComponents fyzicky neexistuje,
        /// ale vždy se generuje ze všech prvků které jsou Selected.
        /// </summary>
        /// <param name="components"></param>
        internal void SelectComponentRemove(IEnumerable<ReportComponent> components)
        {
            foreach (ReportComponent component in components)
                component.SelectClear();
        }
        /// <summary>
        /// Smazání výběru komponent.
        /// Po smazání se provádí metoda this.SelectComponentsAfter() = příprava dat (prázdných!) do reverteru a refresh UI.
        /// </summary>
        /// <returns></returns>
        internal bool SelectComponentClear()
        {
            return this.SelectComponentClear(false);
        }
        /// <summary>
        /// Smazání výběru komponent s možností vynechání metody SelectComponentsAfter() = příprava dat (prázdných!) do reverteru a refresh UI, 
        /// vhodné v případech kdy tu metodu budeme za chvilku volat po další změně Selected.
        /// </summary>
        /// <returns></returns>
        internal bool SelectComponentClear(bool omitAfterAction)
        {
            foreach (ReportComponent component in this.ComponentDict.Values.Where(c => c.Selected))
                component.SelectClear();
            if (!omitAfterAction)
                this.SelectComponentsAfter(true);
            return true;
        }
        /// <summary>
        /// Seznam všech selectovaných komponent.
        /// V Get metodě se vytvoří nový aktuálně platný seznam z komponent, které jsou selectované.
        /// </summary>
        internal List<ReportComponent> SelectedComponents
        {
            get
            {
                List<ReportComponent> result = this.EditorComponents.Where(c => c.Selected).ToList();
                return result;
            }
        }
        /// <summary>
        /// Tato metoda se volá po každé ukončené změně selectovaných komponent.
        /// </summary>
        internal void SelectComponentsAfter(bool selectInComboObjects)
        {
            // this.PrepareToRevertAndPropertyGrid(this.EditorComponents.Where(c => c.Selected).Cast<IRevertItem>());
            List<ReportComponent> selectedComponents = this.EditorComponents.Where(c => c.Selected).ToList();
            if (selectInComboObjects)
            {
                if (selectedComponents.Count == 1)
                    this.ComponentComboSelectItem(selectedComponents[0].ComponentId, true);
                else
                    this.ComponentComboSelectItem(0, true);
            }
            this.ShowInPropertyGrid(selectedComponents);
            this.UIRefresh();
        }
        /// <summary>
        /// Metoda přidá svůj díl informací pro Refresh UI, a to za prvky UI které jsou ovlivněné Selectováním
        /// </summary>
        /// <param name="itemStateList">Pole informací o položkách UI a jejich aktuálním stavu</param>
        /// <param name="selectedCount">Počet vybraných (selected) prvků</param>
        /// <param name="selectedComponents">Pole vybraných (selected) prvků</param>
        private void UIRefreshSelect(List<EditorUIItemState> itemStateList, int selectedCount, List<ReportComponent> selectedComponents)
        {
            bool enableEditor = (this.Form.EditorContent == EditorForm.EditorContentType.Editor);
            bool isAtLeastOne = (selectedCount >= 1);
            bool isAtLeastTwo = (selectedCount >= 2);

            itemStateList.Add(new EditorUIItemState(EditorCommandType.Align, EditorCommandAlignType.AlignHLeft) { Enabled = isAtLeastTwo && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Align, EditorCommandAlignType.AlignHCenter) { Enabled = isAtLeastTwo && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Align, EditorCommandAlignType.AlignHRight) { Enabled = isAtLeastTwo && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Align, EditorCommandAlignType.AlignVTop) { Enabled = isAtLeastTwo && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Align, EditorCommandAlignType.AlignVMiddle) { Enabled = isAtLeastTwo && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Align, EditorCommandAlignType.AlignVBottom) { Enabled = isAtLeastTwo && enableEditor });

            itemStateList.Add(new EditorUIItemState(EditorCommandType.Group, EditorCommandGroupType.ObjectGroup) { Enabled = isAtLeastTwo && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Group, EditorCommandGroupType.ObjectUnGroup) { Enabled = isAtLeastOne && enableEditor });

            itemStateList.Add(new EditorUIItemState(EditorCommandType.ZOrder, EditorCommandZOrderType.ZOrderTop) { Enabled = isAtLeastOne && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.ZOrder, EditorCommandZOrderType.ZOrderHigher) { Enabled = isAtLeastOne && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.ZOrder, EditorCommandZOrderType.ZOrderLower) { Enabled = isAtLeastOne && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.ZOrder, EditorCommandZOrderType.ZOrderBottom) { Enabled = isAtLeastOne && enableEditor });

            itemStateList.Add(new EditorUIItemState(EditorCommandType.Equal, EditorCommandEqualType.EqualIntervalX) { Enabled = isAtLeastTwo && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Equal, EditorCommandEqualType.EqualIntervalY) { Enabled = isAtLeastTwo && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Equal, EditorCommandEqualType.EqualSizeHeight) { Enabled = isAtLeastTwo && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Equal, EditorCommandEqualType.EqualSizeWidth) { Enabled = isAtLeastTwo && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Equal, EditorCommandEqualType.EqualSpacingX) { Enabled = isAtLeastTwo && enableEditor });
            itemStateList.Add(new EditorUIItemState(EditorCommandType.Equal, EditorCommandEqualType.EqualSpacingY) { Enabled = isAtLeastTwo && enableEditor });
        }
        #endregion
        #region Selectování Bands
        /// <summary>
        /// Metoda aktivuje daný Band = zobrazí jej v PropertyGridu
        /// </summary>
        /// <param name="reportBand"></param>
        internal void SelectBand(ReportBand reportBand)
        {
            this.ShowInPropertyGrid(reportBand);
        }
        #endregion
        #region Práce s Gridem
        /// <summary>
        /// Vygeneruje vizuální souřadnice gridu pro dané vizuální souřadnice prvku (Band).
        /// </summary>
        /// <param name="args"></param>
        /// <param name="visualBound"></param>
        /// <param name="visibleArea"></param>
        /// <param name="gridXCoords"></param>
        /// <param name="gridYCoords"></param>
        internal void ReportGridCreateCoordinates(EditPaintEventArgs args, RectangleF visualBound, RectangleF visibleArea, out int[] gridXCoords, out int[] gridYCoords)
        {
            this.ReportGrid.GridCreateCoordinates(args, visualBound, visibleArea, out gridXCoords, out gridYCoords);
        }
        /// <summary>
        /// Vrátí stringový otisk aktuálních parametrů gridu, do nějž navíc vloží i předané vizuální parametry.
        /// </summary>
        /// <param name="visualBound"></param>
        /// <param name="visibleArea"></param>
        /// <param name="zoom"></param>
        /// <returns></returns>
        internal string ReportGridGetCoordMark(RectangleF visualBound, RectangleF visibleArea, float zoom)
        {
            return this.ReportGrid.GridGetCoordMark(visualBound, visibleArea, zoom);
        }
        /// <summary>
        /// Uživatel vybral (LeftClick) objekt Pravítko = zobrazíme vlastnosti ReportGridu (v okně PropertyGridu).
        /// </summary>
        /// <param name="args"></param>
        internal void ReportGridSelect(EditInteractiveEventArgs args)
        {
            if (this.HasForm)
                this.Form.PropertyPanelSelectedObject = this.ReportGrid;
        }
        /// <summary>
        /// Režim zobrazování ReportGridu
        /// </summary>
        internal ShowGridType ReportGridType { get { return this.ReportGrid.ShowGrid; } }
        #endregion
        #endregion
    }
}
